package com.dc.flow.ui

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.dc.flow.ext.processBaseResult
import com.dc.flow.network.BaseResult
import com.dc.flow.state.IBaseState
import com.dc.flow.state.LoadingState
import com.dc.flow.state.PageState
import com.dc.flow.vm.BaseFlowViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch

abstract class BaseFlowActivity<VM : BaseFlowViewModel> : AppCompatActivity(), IBaseState {

    protected open lateinit var mViewModel: VM

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(layoutId())
        mViewModel = createViewModel()
        initView()
        initData()
        collectData()
        createObserver()
    }

    private fun collectData() {
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                launch { collectPageState() }
                launch { collectLoadingState() }
            }
        }
    }

    private suspend fun collectPageState() {
        mViewModel.pageState.collect {
            when (it) {
                is PageState.Loading -> showPageLoading(it.loadingMsg)
                is PageState.ShowContent -> showPageContent()
                is PageState.Empty -> showPageEmpty(it.emptyString)
                is PageState.Error -> showPageError(it.exception.message)
            }
        }
    }

    private suspend fun collectLoadingState() {
        mViewModel.loadingState.collect {
            when (it) {
                is LoadingState.Show -> showLoading(it.tipsWord)
                is LoadingState.Hide -> dismissLoading()
            }
        }
    }

    protected fun changeLoadingState(isShowing: Boolean, tipsWord: String = "") {
        mViewModel.changeLoadingState(isShowing, tipsWord)
    }

    protected fun changePageState(pageState: PageState<String>) {
        mViewModel.changePageState(pageState)
    }

    /**
     * 简化BaseResult的处理逻辑。用法：
     * lifecycleScope.launch {
     *      repeatOnLifecycle(Lifecycle.State.STARTED) {
     *          .process(...)
     *      }
     * }
     *
     * @param loadingMsg 加载转圈显示的文本
     * @param errorMsg 请求失败的问本
     * @param loading 加载中的回调
     * @param error 错误回调
     * @param scope 作用域
     * @param success 成功的处理逻辑
     */
    protected fun <T> Flow<BaseResult<T>>.process(
        loadingMsg: String = "",
        errorMsg: String = "",
        loading: (Boolean) -> Unit = { state -> changeLoadingState(state, loadingMsg) },
        error: (Throwable) -> Unit = { error -> processError(error, errorMsg) },
        scope: CoroutineScope,
        success: (T) -> Unit
    ) {
        scope.launch {
            processBaseResult(loading = loading, error = error, success = success)
        }
    }


    override fun showPageContent() {

    }

    override fun showPageLoading(msg: String) {

    }

    override fun showPageEmpty(msg: String) {

    }

    override fun showPageError(errorMsg: String?) {

    }

    override fun showLoading(msg: CharSequence) {

    }

    override fun dismissLoading() {

    }

    abstract fun createViewModel(): VM

    /**
     * 没有DataBinding/ViewBinding的布局id
     */
    protected abstract fun layoutId(): Int

    protected abstract fun initView()

    protected abstract fun initData()

    /**
     * 创建数据观察者
     */
    protected open fun createObserver() {
    }

    protected fun processError(error: Throwable, errorMsg: String) {

    }
}